package org.dandsoft.blackbox.laboratory.tools;

import org.dandsoft.blackbox.snn.*;

import javax.jcr.*;
import java.util.Map;
import java.util.Hashtable;

/**
 * Created by IntelliJ IDEA.
 * User: dvilyuzhanin
 * Date: 28.03.2009 17:30:49
 */
public class JCRMonitorStorage implements EnviromentMonitor {

    public final static String TIME_FRAMES_NODE = "time";
    public final static String ENVIROMENT_STATE_NODE = "enviroment";
    public final static String NEW_TRIGGERS_NODE = "newTriggers";
    public final static String USEFUL_USING_NODE = "usefulUsging";
    public final static String INPUTS_NODE = "inputs";
    public final static String OUTPUTS_NODE = "outputs";

    private Session session;

    public static JCRMonitorStorage createRepository(String url) {
        JCRMonitorStorage storage = new JCRMonitorStorage();

        storage.setSession(JCRRepositorySingelton.getInstance().getSession(url));
        return storage;
    }

    private Node getNodeTimeNode(Time time, boolean isCreate) throws RepositoryException {
        Node root = null;
        try {
            root = session.getRootNode().getNode(TIME_FRAMES_NODE);
        } catch (PathNotFoundException e) {
            root = session.getRootNode().addNode(TIME_FRAMES_NODE);
        }
        Node timeNode;
        try {
            timeNode = root.getNode(Long.toString(time.getValue()));
        } catch (PathNotFoundException e) {
            if (isCreate) {
                Property countPoperty = null;
                try {
                    countPoperty = root.getProperty("count");
                    root.setProperty("count", countPoperty.getLong() + 1);
                } catch (RepositoryException e1) {
                    root.setProperty("count", 1);
                }

                Property maxTime = null;
                try {
                    maxTime = root.getProperty("maxTime");
                    root.setProperty("maxTime",
                            maxTime.getLong() > time.getValue() ?
                                    maxTime.getLong() : time.getValue());
                } catch (RepositoryException e1) {
                    root.setProperty("maxTime", time.getValue());
                }

                timeNode = root.addNode(Long.toString(time.getValue()));
                timeNode.addNode(ENVIROMENT_STATE_NODE);
                timeNode.addNode(NEW_TRIGGERS_NODE);
                timeNode.addNode(USEFUL_USING_NODE);
                timeNode.addNode(INPUTS_NODE);
                timeNode.addNode(OUTPUTS_NODE);
            } else {
                return null;
            }
        }
        return timeNode;
    }

    private Node getEnviromentNode(Time time, boolean isCreate) {
        try {
            Node timeNode = getNodeTimeNode(time, isCreate);
            if (timeNode != null) {
                return timeNode.getNode(ENVIROMENT_STATE_NODE);
            } else {
                return null;
            }
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }

    private Node getNewTriggersNode(Time time, boolean isCreate) {
        try {
            Node timeNode = getNodeTimeNode(time, isCreate);
            if (timeNode != null) {
                return timeNode.getNode(NEW_TRIGGERS_NODE);
            } else {
                return null;
            }
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }

    private Node getInputsNode(Time time, boolean isCreate) {
        try {
            Node node = getNodeTimeNode(time, isCreate);
            if (node != null) {
                return node.getNode(INPUTS_NODE);
            } else {
                return null;
            }
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }

    private Node getOutputsNode(Time time, boolean isCreate) {
        try {
            Node node = getNodeTimeNode(time, isCreate);
            if (node != null) {
                return node.getNode(OUTPUTS_NODE);
            } else {
                return null;
            }
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }

    private Node getUsefulUsingNode(Time time, boolean isCreate) {
        try {
            Node node = getNodeTimeNode(time, isCreate);
            if (node != null) {
                return node.getNode(USEFUL_USING_NODE);
            } else {
                return null;
            }
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }

    private void setMaxObjectId(int size) throws RepositoryException {
        Node node = session.getRootNode().getNode(TIME_FRAMES_NODE);
        Property maxSize;
        try {
            maxSize = node.getProperty("maxObjectId");
            if (size > maxSize.getLong()) {
                node.setProperty("maxObjectId", size);
            }
        } catch (PathNotFoundException e) {
            node.setProperty("maxObjectId", size);
        }
    }

    public long getTimesCount() {
        try {
            return session.getRootNode().getNode(TIME_FRAMES_NODE).getProperty("count").getLong();
        } catch (PathNotFoundException e) {
            e.printStackTrace();
            return -1;
        } catch (RepositoryException e) {
            e.printStackTrace();
            return -1;
        }
    }

    public int getMaxObjectId() {
        try {
            Node node = session.getRootNode().getNode(TIME_FRAMES_NODE);
            return (int) node.getProperty("maxObjectId").getLong();
        } catch (RepositoryException e) {
            e.printStackTrace();
            return -1;
        }
    }

    public void logEnviroment(Time time, Map<ObjectId, Signal> env) {
        try {
            Node envNode = getEnviromentNode(time, true);

            
            Property size;
            try {
                size = envNode.getProperty("size");
                envNode.setProperty("size", size.getLong() + env.size());
            } catch (PathNotFoundException e) {
                envNode.setProperty("size", env.size());
            }

            int max = -1;
            for (Map.Entry<ObjectId, Signal> entry : env.entrySet()) {
                Signal signal = entry.getValue();
                StoreUtil.storeSignal(envNode, signal);
                if (max == -1) {
                    max = entry.getKey().getValue();
                } else if (max < entry.getKey().getValue()) {
                    max = entry.getKey().getValue();
                }
            }
            setMaxObjectId(max);
            session.save();
        }

        catch (Throwable e) {
            e.printStackTrace();
            try {
                session.refresh(false);
            } catch (RepositoryException e1) {
                e1.printStackTrace();
            }
        }

    }

    public Map<ObjectId, Signal> getEnviroment(Time time) {
        try {
            Node envNode = getEnviromentNode(time, false);
            return StoreUtil.restoreSignals(envNode);
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }

    public void logNewSigal(Time time, Signal signal) {

    }

    public void logUsefulUsing(Time time, Signal signal) {
        try {
            Node node = getUsefulUsingNode(time, true);
            StoreUtil.storeSignal(node, signal);
            session.save();
        } catch (Throwable e) {
            try {
                session.refresh(false);
            } catch (RepositoryException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    public Map<ObjectId, Signal> getUsefulUsings(Time time) {
        try {
            Node node = getUsefulUsingNode(time, true);
            Map<ObjectId, Signal> result = StoreUtil.restoreSignals(node);
            session.save();
            return result;
        } catch (Throwable e) {
            e.printStackTrace();
            return null;
        }
    }

    public void logAnomaly(Time time, Signal signal) {

    }

    public void logNewTrigger(Time time, Trigger newTrigger) {
        try {
            Node newTriggersNode = getNewTriggersNode(time, true);
            StoreUtil.storeTrigger(newTriggersNode, newTrigger);
            session.save();
        } catch (Throwable e) {
            try {
                session.refresh(false);
            } catch (RepositoryException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    public void logTrigger(Time time, Trigger trigger) {

    }

    public Map<ObjectId, Trigger> getNewTriggers(Time time) {
        try {
            Map<ObjectId, Trigger> result = new Hashtable<ObjectId, Trigger>();
            Node newTriggersNode = getNewTriggersNode(time, true);
            NodeIterator nodes = newTriggersNode.getNodes();
            while (nodes.hasNext()) {
                Node node = nodes.nextNode();
                Trigger trigger = StoreUtil.restoreTrigger(node);
                result.put(trigger.getId(), trigger);
            }

            return result;
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }

    }

    public void logInput(Time time, Signal signal) {
        try {
            Node inputsNode = getInputsNode(time, true);
            StoreUtil.storeSignal(inputsNode, signal);
            session.save();
        } catch (Throwable e) {
            try {
                session.refresh(false);
            } catch (RepositoryException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    public Map<ObjectId, Signal> getInputs(Time time) {
        try {
            Node inputsNode = getInputsNode(time, false);
            return StoreUtil.restoreSignals(inputsNode);
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }

    public void logOutput(Time time, Signal signal) {
        try {
            Node outputsNode = getOutputsNode(time, true);
            StoreUtil.storeSignal(outputsNode, signal);
            session.save();
        } catch (Throwable e) {
            try {
                session.refresh(false);
            } catch (RepositoryException e1) {
                e1.printStackTrace();
            }
            e.printStackTrace();
        }
    }

    public Map<ObjectId, Signal> getOutputs(Time time) {
        try {
            Node outputsNode = getOutputsNode(time, false);
            return StoreUtil.restoreSignals(outputsNode);
        } catch (RepositoryException e) {
            throw new RuntimeException(e);
        }
    }

    public Session getSession() {
        return session;
    }

    public void setSession(Session session) {
        this.session = session;
    }


}
